package actions;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;
import org.apache.struts.actions.DispatchAction;

import beans.AbstractPagedQuery;

/**
 * 实现分页查询的Action抽象类
 * @author zhuwguo
 */
public abstract class AbstractPagedQueryAction extends DispatchAction {
    /**
     * 默认的分页显示时显示的分页超链接数量.
     */

    private static final int DEFAULT_INDEXPAGES = 6;

    /**
     * 默认的每页的记录数量.
     */
    private static final int DEFAULT_ITEMS_PER_PAGE = 20;

    /**
     * 取得pager标签的INDEX_PAGES的maxIndexPages属性值,即分页显示时显示的分页超链接数量.
     */
    private int indexPages = DEFAULT_INDEXPAGES;

    /**
     * 一页显示多少纪录项.
     */
    private int itemsPerPage = DEFAULT_ITEMS_PER_PAGE;

    /**
     * 最大导出的纪录个数
     */
    private int maxExportRecords = 1000;

    /**
     * 返回查询Action的url , 缺省为request.getRequestURI()，不带任何参数.
     * @param request httpRequest
     * @return Returns the url.
     */
    protected String getUrl(final HttpServletRequest request) {
        return request.getRequestURI();
    }

    /**
     * 分页查询
     * @param form actionForm
     * @param mapping struts mapping
     * @param request HTTP request
     * @param response HTTP response
     * @return actionForward
     * @throws Exception any exception
     */
    @SuppressWarnings("unchecked")
    public ActionForward doQuery(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        // 确定查询第几页
        int pagerOffset = 0;

        if (null != request.getParameter("pager.offset")
                && !"".equals(request.getParameter("pager.offset"))) {
            pagerOffset = Integer.valueOf(request.getParameter("pager.offset"));
        }
        // 确定每页显示几条数据
        int pageSize = itemsPerPage;
        if (null != request.getParameter("pagesize")
                && !"".equals(request.getParameter("pagesize"))) {
            pageSize = Integer.valueOf(request.getParameter("pagesize"));
            if (pageSize <= 0) {
                pageSize = this.getItemsPerPage();
            }
        }

        // 确定是否查询总页数
        String sumParm = "sum";
        int totalCount = -1;
        if (null != request.getParameter(sumParm)) {
            totalCount = Integer.valueOf(request.getParameter(sumParm));
        }

        // 调用doPagedQuery()进行查询
        AbstractPagedQuery pagedQuery = doPagedQuery(form, request, totalCount,
                pagerOffset, pageSize);
        if (pagedQuery.getTotalCount() == 0) {// 没有查询结果记录时，返回到该页面
            String msg = getResources(request).getMessage(
                    "info.noMatchedSearchResult");
            request.setAttribute("EMPTY_QUERY_RESULT_MSG", msg);
            return this.getEmptyResultForward(mapping);
        } else {
            // 查询的记录总数
            request.setAttribute(sumParm, pagedQuery.getTotalCount());
            // 每页显示的条数
            request.setAttribute("pagesize", pageSize);
            // 分页显示时显示的分页数量 ，对应<pg:pager>标签的maxIndexPages的值
            request.setAttribute("indexPages", indexPages);
            // 查询Action的url地址
            request.setAttribute("urlaction", getUrl(request));
            // 查询参数字符串（不包含“?“的符合URL标准的参数名=值的字符串）
            String qryParms = getUrlParams(form, request) + "&sum="
                    + pagedQuery.getTotalCount();
            request.setAttribute("urlParams", qryParms + "&pagesize="
                    + pageSize);
            // 这个不包含pageSize的变量共翻页标签用来设置每页包含多少条纪录信息.
            request.setAttribute("urlParamWithoutPageSize", qryParms);
            // 查询结果集放入request中的名字常量
            request.setAttribute("ITEMS_COLLECTION", pagedQuery
                    .getQueryResults());
            return mapping.findForward("success");
        }

    }

    /**
     * 查询结果导出
     * @param form actionForm
     * @param mapping struts mapping
     * @param request HTTP request
     * @param response HTTP response
     * @return actionForward
     * @throws Exception any exception
     */
    public ActionForward doExport(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        int pagerOffset = 0;
        int pageSize = maxExportRecords;
        int totalCount = -1;

        // 调用doPagedQuery()进行查询
        AbstractPagedQuery<?> pagedQuery = doPagedQuery(form, request,
                totalCount, pagerOffset, pageSize);
        totalCount = (int) pagedQuery.getTotalCount();
        if (totalCount > pageSize) {
            ActionMessages errMsgs = new ActionMessages();
            errMsgs.add("", new ActionMessage(
                    "error.exceedMaxExportRecordCount", totalCount, pageSize));
            this.saveErrors(request, errMsgs);
            return mapping.findForward("error.back");

        } else if (totalCount == 0) {// 没有查到数据
            ActionMessages errMsgs = new ActionMessages();
            errMsgs.add("", new ActionMessage("error.noExportRecord"));
            this.saveErrors(request, errMsgs);
            return mapping.findForward("error.back");

        } else {
            // 子类完成实际的数据导出功能，需要导出的数据已经在pagedQuery.queryResults中了
            return doDataExport(mapping, form, request, response, pagedQuery);
        }
    }

    /**
     * 完成实际的数据导出，数据已经在pagedQuery中了
     * @param form action form
     * @param request http request
     * @param response http response
     * @param mapping action mapping
     * @param pagedQuery 每页多少条纪录
     * @return AbstractPagedQuery 已经包含有查询结果的PagedQuery对象
     * @throws Exception any exception
     */
    protected ActionForward doDataExport(ActionMapping mapping,
            ActionForm form, HttpServletRequest request,
            HttpServletResponse response, AbstractPagedQuery<?> pagedQuery)
            throws Exception {
        // 子类实现,如果需要导出到excel 参考 UserAccountMainAction.exportUserAccountsToExcel
        return null;
    }

    /**
     * 当没有查询结果记录时，返回到该页面，默认为返回查询Input界面.
     * @param mapping action mapping
     * @return action forward
     */
    protected ActionForward getEmptyResultForward(ActionMapping mapping) {
        return mapping.getInputForward();
    }

    /**
     * 构造(解析）分页查询对象，需要从request中获取查询参数并调用业务服务对象完成分页查询.
     * @param form action form
     * @param request http request
     * @param totalCount 纪录总条数
     * @param pagerOffset 查询第几页，从0开始
     * @param itemsPerPage 每页多少条纪录
     * @return AbstractPagedQuery 已经包含有查询结果的PagedQuery对象
     * @throws Exception any exception
     */
    @SuppressWarnings("unchecked")
    public abstract AbstractPagedQuery doPagedQuery(ActionForm form,
            HttpServletRequest request, int totalCount, int pagerOffset,
            int itemsPerPage) throws Exception;

    /***************************************************************************
     * 从request中提取查询参数并转化为url形式的字符串.
     * @param form action form
     * @param request http request
     * @return url字符串形式的param
     **************************************************************************/
    @SuppressWarnings("unchecked")
    protected String getUrlParams(ActionForm form, HttpServletRequest request) {
        Map reqParamMap = request.getParameterMap();
        return buildQueryString(reqParamMap,
                new String[] { "pager.offset", "sum", "pageSize" }).toString();
    }

    /**
     * Builds a query string from a given map of parameters.
     * @param paramMap A map of parameters
     * @param excludeParamNames 需要排除的参数
     * @return query string (with no leading "?")
     */
    @SuppressWarnings("unchecked")
    protected static StringBuilder buildQueryString(Map paramMap,
            String[] excludeParamNames) {
        StringBuilder aReturn = new StringBuilder();
        Set aEntryS = paramMap.entrySet();
        Iterator aEntryI = aEntryS.iterator();
        while (aEntryI.hasNext()) {
            Map.Entry aEntry = (Map.Entry) aEntryI.next();
            Object theValue = aEntry.getValue();

            if (theValue != null) {
                String key = (String) aEntry.getKey();
                boolean isExcudeKey = false;
                for (String excludeKey : excludeParamNames) {
                    if (key.equalsIgnoreCase(excludeKey)) {
                        isExcudeKey = true;
                        break;
                    }
                }
                if (isExcudeKey) {
                    continue;
                }
                if (theValue instanceof String) {
                    append(key, (String) theValue, aReturn);
                } else if (theValue instanceof String[]) {
                    String[] aValues = (String[]) theValue;

                    for (String val : aValues) {
                        append(key, val, aReturn);
                    }
                } else {
                    append(key, theValue.toString(), aReturn);
                }
            }
        }
        return aReturn;
    }

    /**
     * Appends new key and value pair to query string.
     * @param key parameter name
     * @param value value of parameter
     * @param queryString existing query string
     * @return query string (with no leading "?")
     */
    protected static StringBuilder append(String key, String value,
            StringBuilder queryString) {
        if (queryString.length() > 0) {
            queryString.append("&");
        }

        try {
            queryString.append(URLEncoder.encode(key, "UTF-8"));
            queryString.append("=");
            queryString.append(URLEncoder.encode(value, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        return queryString;
    }

    /**
     * @return Returns the indexPages.
     */
    public int getIndexPages() {
        return indexPages;
    }

    /**
     * @param pIndexPages The indexPages to set.
     */
    public void setIndexPages(int pIndexPages) {
        this.indexPages = pIndexPages;
    }

    /**
     * @return Returns the itemsPerPage.
     */
    public int getItemsPerPage() {
        return itemsPerPage;
    }

    /**
     * @param pItemsPerPage The itemsPerPage to set.
     */
    public void setItemsPerPage(int pItemsPerPage) {
        if (pItemsPerPage <= 0) {
            this.itemsPerPage = DEFAULT_ITEMS_PER_PAGE;
        } else {
            this.itemsPerPage = pItemsPerPage;
        }
    }

    /**
     * @param pMaxExportRecords maxExportRecords
     */
    public void setMaxExportRecords(int pMaxExportRecords) {
        maxExportRecords = pMaxExportRecords;
    }
}
